<?php
// SuperConstructor
// Copyright Alex McClung, 2007
// Version 0.25a, October 2007
// http://mcclungalexander.com
// 
// SuperConstructor is a content management system framework for PHP 5, Smarty, and MySQL.
// For more information, please see: http://code.google.com/p/superconstructor
//
// This software is freely distributable under the terms of the GPL Version 3 license.

//===================================================================================
// Constants
define('SC_BASE',      $_SERVER['DOCUMENT_ROOT'] . "/_smarty/"); // This is the base path where all of your site's smarty files live.
define('SESSION_USER', "sc_user"                              ); // Session variable that the currently-logged-in user is stored in
	
require_once("Smarty/Smarty.class.php");

class SuperConstructor extends Smarty
{
	//===================================================================================
	// Private Variables
	private $_user; // Instance of the SC_User who is currently logged in.
	private $_sql;  // SQL object
	
	//===================================================================================
	// Public Variables
	
	//===================================================================================
	// Public Functions
	public $config = NULL; // Configuration variables get stored in this object. Variables are loaded from the site config file (sc.conf)
	
	//-----------------------------------------------------------------------------------
	// Class constructor
	public function __construct()
	{
//		MQ::Disable(MQ_DEBUG);
		
		// Call the main smarty constructor
		$this->Smarty();
		
		// Use a global variable to store the SQL connection so we don't have
		// to reconnect every time the class is instantiated.
		if(!isset($GLOBALS['_sc_sql']))
			$this->Reconnect();
		else
			$this->_sql = $GLOBALS['_sc_sql'];
			
		// Make sure a session is active
		session_start();
		
		// Setup our directories. This should be set to wherever all of the smarty-related
		// files for SuperConstructor (and your site) live.
		$this->template_dir  = SC_BASE . "templates/";
		$this->compile_dir 	 = SC_BASE . "templates_c/";
		$this->cache_dir 	 = SC_BASE . "cache/";
		$this->config_dir 	 = SC_BASE . "configs/";
		$this->plugins_dir[] = SC_BASE . "plugins/";
		
		// Load the config file
		$this->config_load("sc.conf"); // This loads it for the template
		$this->_load_config_vars( parse_ini_file($this->config_dir . "sc.conf", true) ); // And this loads it into this object
		
		// If a user is logged in, go ahead and give it to the template
		$user = $this->GetUser();
		if($user !== false)
		{
			$this->_user = $user;
			$this->assign("_user", $this->_user );
			
			// Also, load the bindings
			$this->LoadBindings();
		}
	}
	
	/*!
		\brief (Re)connects to the database. Handy if you want to preserve SimpleDBO objects in $_SESSION
		\returns nothing
	*/
	public function Reconnect()
	{
		$this->_sql = new SQL();
		$GLOBALS['_sql'] = $this->_dbo_sql;
	}
	
	//-----------------------------------------------------------------------------------
	// Logs a user in
	// $username   username of the person to log in
	// $md5_pw     pre-encrypted password
	// Returns True if the person's login credentials are okay, false if not
	public function LogIn($username, $md5_pw)
	{
		$user = new SC_User();
		if( $user->LogIn($username, $md5_pw) )
		{
			// Start the session and remember who's logged in
			$_SESSION[SESSION_USER] = $user->uid;
			$this->_user = $user;
			return true;
		}
		MQ::Error("Invalid username or password.");
		return false;
	}
	
	//-----------------------------------------------------------------------------------
	// Logs the user out
	public function LogOut()
	{
		// Are we logged in?
		if( isset($_SESSION[SESSION_USER]) )
		{
			// Make sure it's good and dead
			session_destroy();
			unset( $_SESSION[SESSION_USER] );
			unset( $this->_user );
		}
	}
	
	//-----------------------------------------------------------------------------------
	// Gets the SC_User instance of the user who is logged in. Will return FALSE
	// if nobody is logged in.
	public function GetUser()
	{
		// Is the session variable even set?
		if( isset($_SESSION[SESSION_USER]) )
		{
			// Make sure it's a valid user id
			$user = new SC_User( $_SESSION[SESSION_USER] );
			// $user will be false if the UID is invalid
			return $user;
		}
		return false;
	}
	
	//-----------------------------------------------------------------------------------
	// brief Returns a pre-configured PHPMailer object
	public function GetMailer()
	{
		$m = new PHPMailer();
		
		// Apply settings from the config file
		$m->Mailer = $this->config->Mail->MailerType;
		
		if($m->Mailer == "sendmail")
		{
			$m->Sendmail = $this->config->Mail->SendMail_Path;
		}
		else if($m->Mailer == "SMTP")
		{
			$m->Host     = $this->config->Mail->SMTP_Host;
			$m->Port     = $this->config->Mail->SMTP_Port;
			$m->SMTPAuth = $this->config->Mail->SMTP_Auth;
			$m->Username = $this->config->Mail->SMTP_Username;
			$m->Password = $this->config->Mail->SMTP_Password;
		}

		return $m;
	}

	
	//-----------------------------------------------------------------------------------
	// Loads POST (or GET) data, filtering out SuperConstructor-reserved inputs and 
	// performing form validation. Errors will be sent to the MessageQueue (MQ). By
	// default, data will be loaded into global variables. If an object is passed in as
	// the second parameter, data will instead be loaded into that object.
	public function LoadFormData($post, $objref=NULL)
	{
		// Load post data
		foreach($post as $varname=>$value)
		{
			MQ::Debug("Loading $varname ($value) into object.");
			// Ignore everything that begins with an underscore (because it's used by the system)
			if(substr($varname, 0, 1) == "_")
				continue;
				
			// Look in the $_POST['_required'] array, if _colname is in there, it's required
			// So check to make sure it's not empty
			// TODO: Put '_required' into a constant somewhere. Use it in the templates.
			if(isset($post['_required']) && array_search($varname, $post['_required']) !== false && $value == "")
				MQ::Push("<strong>$varname</strong> is required.", MQ_ERROR);
			else
			{
				// Capture the variable for ourselves...
				if($objref == NULL)
				{
					global $$varname;
					$$varname = $value;
				}
				else
				{
					$objref->$varname = $value;
				}
				
				//...AND give it to the template
				$this->assign($varname, $value);
			}
		}
	}
	
	
	//-----------------------------------------------------------------------------------
	// Lists all tables in the current database.
	// $showprivate (bool) Whether to show private tables or not. (Default: False)
	public function ListTables($showprivate=false)
	{
		$colname = "Tables_in_{$this->_sql->db}";
		$query = ($showprivate) ? "SHOW TABLES" : "SHOW TABLES WHERE {$colname} NOT LIKE 'sc_%'";
		$tables = array();
		
		$this->_sql->Query($query);
		while($row = $this->_sql->GetObject())
		{
			$tables[] = $row->$colname;
		}
		
		return $tables;
	}
	
	//-----------------------------------------------------------------------------------
	// Lists all the forms present in the user-created forms directory (usually smarty/admin/forms/)
	public function ListForms()
	{
		$basedir = $this->template_dir."admin/forms/";
		$bdlen   = strlen($basedir);
		$raw = glob($basedir . "*.tpl");
		$forms = array();
		
		foreach($raw as $form)
		{
			$forms[] = substr($form, $bdlen);
		}
		
		return $forms;
	}
	
	//-----------------------------------------------------------------------------------
	// Loads the Form->Table bindings from the database, dumps them into the template variable
	// _bindings
	public function LoadBindings()
	{
		$b      = new SC_Binding();
		$search = new SimpleDBOSearch($b);
		$result = $search->Search();
		$rows   = array();
		
		while($b = $result->Next(new SC_Binding()))
			$rows[$b->map_id] = $b->name;
		
		// Cleanup
		unset($b, $search, $result);
		
		// Get the bindings to the template
		$this->assign("_bindings", $rows);
	}

	//===================================================================================
	// Private Functions
	
	private function _load_config_vars($vars, $section=NULL)
	{
		foreach($vars as $varname=>$value)
		{
			if(is_array($value))
			{
				if($section == NULL)
					$this->_load_config_vars($value, $varname);
				else
					$this->_load_config_vars($value, "{$section}->$varname}");
			}
			else if($section != NULL)
				$this->config->$section->$varname = $value;
			else
				$this->config->$varname = $value;
		}
	}
	
	//===================================================================================
	// Accessors
	
	//-----------------------------------------------------------------------------------
	// Special accessor method for retrieving speciailized data as a class property
	public function __get($varname)
	{
		// Provide read-only access to private properties
		switch($varname)
		{
			case "user": return $this->_user; break;
			default: break;
		}
		return $this->$varname;
	}
	
	//-----------------------------------------------------------------------------------
	// Special accessor method for assigning speciailized data as a class property
	public function __set($varname, $value)
	{
		// Provide read-only access to private properties
		switch($varname)
		{
			case "user": $this->_user    = $value; break;
			default:     $this->$varname = $value; break;
		}
	}
}
?>